﻿// <copyright file="TTableStorageQuery.cs" company="N/A">
//     Copyright (c) Nicholas Barrett and Benjamin Kasten.  All rights reserved.
//
//     This program is free software; you can redistribute it and/or modify it
//     under the terms of the GNU General Public License as published by the
//     Free Software Foundation; either version 2 of the License, or (at your
//     option) any later version.
//
//     This program is distributed in the hope that it will be useful, but
//     WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
//     Public License for more details.
//
//     You should have received a copy of the GNU General Public License along
//     with this program; if not, write to the Free Software Foundation, 
//     Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// </copyright>

namespace BetterAzureTableStorage
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    /// <summary>
    /// Evaluates queries against TableStorage wherein the type (or base type) of the data is known.
    /// </summary>
    /// <typeparam name="T">The type or base type of data being queried.</typeparam>
    public class TTableStorageQuery<T> : IQueryable<T>
    {
        public Func<PropertyInfo, string> RenameProperty { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="TTableStorageQuery&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="tableName">Name of the table.</param>
        /// <param name="createTableStorageEngine">The function to create a table storage engine.</param>
        /// <param name="renamePropertyFunc">The rename property function.</param>
        public TTableStorageQuery(TTableStorageInfo tableInfo, Func<string, TTableStorageEngine> createTableStorageEngine, Func<PropertyInfo, string> renamePropertyFunc, TTableStorageDataContext context)
        {
            this.RenameProperty = renamePropertyFunc;
            this.Provider = new TTableStorageQueryProvider(tableInfo, createTableStorageEngine, this.RenameProperty, context);
            this.Expression = Expression.Constant(this);
        }

        public TTableStorageQuery<T> Expand(string path)
        {
            return (TTableStorageQuery<T>)this.Provider.CreateQuery<T>(new TExpandExpression(typeof(IQueryable<T>), this.Expression, path));
        }

        /// <summary>
        /// Initializes a new instance of the TTableStorageQuery class.  This constructor is called by Provider.CreateQuery().
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="expression">The expression.</param>
        /// <param name="renamePropertyFunc">The rename property function.</param>
        public TTableStorageQuery(TTableStorageQueryProvider provider, Expression expression, Func<PropertyInfo, string> renamePropertyFunc = null)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            this.RenameProperty = renamePropertyFunc;

            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentOutOfRangeException("expression");
            }

            this.Provider = provider;
            this.Expression = expression;
        }

        /// <summary>
        /// Gets the enumerator.
        /// </summary>
        /// <returns>The enumerator</returns>
        public IEnumerator<T> GetEnumerator()
        {
            return this.Provider.Execute<IEnumerable<T>>(Expression).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this.Provider.Execute(Expression)).GetEnumerator();
        }

        /// <summary>
        /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of <see cref="T:System.Linq.IQueryable"/> is executed.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.Type"/> that represents the type of the element(s) that are returned when the expression tree associated with this object is executed.</returns>
        public Type ElementType
        {
            get
            {
                return typeof(T);
            }
        }

        /// <summary>
        /// Gets the expression tree that is associated with the instance of <see cref="T:System.Linq.IQueryable"/>.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="T:System.Linq.Expressions.Expression"/> that is associated with this instance of <see cref="T:System.Linq.IQueryable"/>.</returns>
        public Expression Expression { get; private set; }

        /// <summary>
        /// Gets the query provider that is associated with this data source.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="T:System.Linq.IQueryProvider"/> that is associated with this data source.</returns>
        public IQueryProvider Provider { get; private set; }
    }
}
